(function(){
   var allCalendar = [];
   var $ = function(obj) {
     if(typeof obj == "string") {
       return document.getElementById(obj);
     }
     return obj;
   };

   var $n = function(tag) {
     return document.createElement(tag);
   };

   var $e = function(ele, type, fn) {
     if (ele.addEventListener) ele.addEventListener(type, fn, false);
		 else ele.attachEvent('on' + type, ieAttachEvent(fn, ele));
   };

   function ieAttachEvent(fn, ele) {
     return function() {
       fn.apply(ele);
     };
   };

   function parsePX(px) {
     return parseInt(px.substr(0, px.length - 2));
   }

   var $off = function(element) {
     var valueT = 0, valueL = 0;
     do {
       valueT += element.offsetTop  || 0;
       valueL += element.offsetLeft || 0;
       valueT += parsePX(element.style.marginTop || "0px");
       valueT += parsePX(element.style.paddingTop || "0px");
       valueL += parsePX(element.style.marginLeft || "0px");
       valueL += parsePX(element.style.paddingLeft || "0px");
       element = element.offsetParent;
       if (element) {
         if (element.tagName.toUpperCase() == 'BODY') break;
         var p = element.style.position;
        // if (p !== 'static') break;
       }
     } while (element);
     return {
       'left': valueL,
       'top': valueT
     };
   };

   var $dim = function(element) {
     element = $(element);
     var display = element.style.display;
     if (display != 'none' && display != null) { // Safari bug
       return {width: element.offsetWidth, height: element.offsetHeight};
     }

     // All *Width and *Height properties give 0 on elements with display none,
     // so enable the element temporarily
     var els = element.style;
     var originalVisibility = els.visibility;
     var originalPosition = els.style.position;
     var originalDisplay = els.style.display;
     els.visibility = 'hidden';
     els.style.position = 'absolute';
     els.style.display = 'block';
     var originalWidth = element.clientWidth;
     var originalHeight = element.clientHeight;
     els.style.display = originalDisplay;
     els.style.position = originalPosition;
     els.visibility = originalVisibility;
     return {width: originalWidth, height: originalHeight};
   };


   var Calendar = function(type, object, callback, options) {
     var personal = {};
     allCalendar.push(this);
     personal["type"] = type;
     if(type == "POPUP") {
       personal["source"] = $(object);
     }
     else {
       personal["container"] = $(object);
     }
     personal["callback"] = callback;
     personal["curdate"] = new Date();
     personal["seldate"] = new Date();
     personal["pos"] = [0, 0];
     personal["daterange"] = [this.divDate("19700101"), this.divDate("21001230")];
     personal["yearrange"] = [1970, 2100];
     personal["monthrange"] = [1, 12];
     personal["dayrange"] = [1, 31];
     personal["weekdayrange"] = [1, 7];
     personal["width"] = 300;
     personal["parent"] = $(document.body);
     personal["dayspane"] = null;
     personal["shadowifr"] = null;
     personal["pane"] = null;
     personal["monpane"] = null;
     personal["yearpane"] = null;
     personal["align"] = "left";
     personal["shadowpane"] = null;
     personal["montharr"] = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
     personal["weekarr"] = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
     personal["height"] = 150;

     this.get = function(variable) {
       return typeof personal[variable] == "undefined"?null:personal[variable];
     };
     this.set = function(variable, value) {
       if(variable != "options" && typeof personal[variable] != "undefined") {
         personal[variable] = value;
       }
       else {
         throw "Variaible can be set.";
       }
       return this;
     };

     this.initialize(options);
   };

   Calendar.prototype = {
     initialize: function(opts) {
       if(opts != null) {
         var dfdate = opts["defaultdate"];
         if(typeof dfdate != "undefined") {
           this.set("curdate", this.divDate(dfdate));
         }
         if(typeof opts["align"] != "undefined") {
           this.set("align",  opts["align"]);
         }
         if(typeof opts["width"] != "undefined") {
           this.set("width", opts["width"]);
         }
         if(typeof opts["parent"] != "undefined") {
           this.set("parent", opts["parent"]);
         }
         if(typeof opts["weeklabels"] != "undefined") {
           this.set("weekarr", opts["weeklabels"]);
         }
         if(typeof opts["monthlabels"] != "undefined") {
           this.set("montharr", opts["monthlabels"]);
         }

         if(typeof opts["daterange"] != "undefined") {
           this.set("daterange", [
                      this.divDate(opts["daterange"][0]),
                      this.divDate(opts["daterange"][1])
                    ]);
         }
         else {
           if(typeof opts["yearrange"] != "undefined") {
             this.set("yearrange", opts["yearrange"]);
           }
           if(typeof opts["monthrange"] != "undefined") {
             this.set("monthrange", opts["monthrange"]);
           }
           if(typeof opts["dayrange"] != "undefined") {
             this.set("dayrange", opts["dayrange"]);
           }

           if(typeof opts["weekdayrange"] != "undefined") {
             this.set("weekdayrange", opts["weekdayrange"]);
           }
         }
       }

       var className = "c_fixed c_pane";
       var type = this.get("type");
       if(type == "POPUP") {
         className = "c_popup c_pane";
         var hei = this.get("height");
         var ifr = $n("iframe");
         ifr.width = this.get("width");
         ifr.height = hei;
         ifr.setAttribute("frameborder","0");
         ifr.setAttribute("scrolling", "no");
         this.set("shadowifr", ifr);
         ifr.style.cssText = "display:none;position:absolute;background:black";
         ifr.style.filter = 'alpha(opacity=30)';
		     ifr.style.opacity = 0.3;

         var spane = $n("div");
         this.set("shadowpane", spane);
         spane.style.cssText = "display:none;position:absolute;background:black;width:" + this.get("width") + "px;height:" + hei + "px;";
         spane.style.filter = 'alpha(opacity=30)';
		     spane.style.opacity = 0.3;
       }

       var curdate = this.comDate(this.get("curdate"));
       var wid = this.get("width");

       var pane = $n("div");
       pane.className = className;
       pane.style.cssText = "width:" + wid + "px;display:none;";
       var monpan = $n("div");
       monpan.className = "monpan";
       var monpre = $n("span");
       monpre.className = "pre";
       monpre.innerHTML = "&lt;";
       $e(monpre, "click", this.preMonth(this));
       var monnext = $n("span");
       monnext.className = "next";
       monnext.innerHTML = "&gt;";
       $e(monnext, "click", this.nextMonth(this));
       var montxt = $n("span");
       montxt.className = "txt";
       this.set("monpane", montxt);
       montxt.innerHTML = curdate.monstr;
       monpan.appendChild(monpre);
       monpan.appendChild(montxt);
       monpan.appendChild(monnext);

       var ypan = $n("div");
       ypan.className = "ypan";
       var ypre = $n("span");
       ypre.className = "pre";
       ypre.innerHTML = "&lt;";
       $e(ypre, "click", this.preYear(this));
       var ynext = $n("span");
       ynext.className = "next";
       ynext.innerHTML = "&gt;";
       $e(ynext, "click", this.nextYear(this));
       var ytxt = $n("span");
       ytxt.className = "txt";
       this.set("yearpane", ytxt);
       ytxt.innerHTML = curdate.year;
       ypan.appendChild(ypre);
       ypan.appendChild(ytxt);
       ypan.appendChild(ynext);
       var clr = $n("div");
       clr.className = "clear";
       pane.appendChild(monpan);
       pane.appendChild(ypan);
       pane.appendChild(clr);

       var weekpane = $n("div");
       weekpane.className = 'weekpane';
       var dwid = wid - 7 * 4;
       dwid = parseInt(dwid / 7);
       for(var i = 0; i < 7; i++){
         var daypane = $n("div");
         daypane.className = "weekday";
         daypane.style.cssText = "width:" + dwid + "px;";
         daypane.innerHTML = this.get("weekarr")[i];
         weekpane.appendChild(daypane);
       }
       clr = $n("div");
       clr.className = "clear";
       weekpane.appendChild(clr);
       pane.appendChild(weekpane);

       var dayspane = $n("div");
       dayspane.className = 'dayspane';
       pane.appendChild(dayspane);

       if(type != "POPUP") {
         this.get("container").appendChild(pane);
       }
       else {
         this.get("parent").appendChild(pane);
         this.get("parent").appendChild(this.get("shadowpane"));
         this.get("parent").appendChild(this.get("shadowifr"));
         pane.style.position = "absolute";
         var hei = this.get("height");
         pane.style.height = hei + "px";
       }

       this.set("dayspane", dayspane);
       this.set("pane", pane);
       if(this.get("type") == "POPUP") {
         $e(this.get("source"), "click", this.show(this));
         $e($(document.body.parentNode), "click", this.hide(this));
         $e(pane, "click", function(evt) {
              evt = evt || window.event;
              if (evt.stopPropagation) evt.stopPropagation();
		          else evt.cancelBubble = true;
              if (evt.preventDefault) evt.preventDefault();
              else evt.returnValue = false;
            });
       }
       else {
         this.get("pane").style.display = "";
         this.redraw();
       }
     },

     show: function(obj) {
       return function(evt) {
         evt = evt || window.event;
         if (evt.stopPropagation) evt.stopPropagation();
		     else evt.cancelBubble = true;
         if (evt.preventDefault) evt.preventDefault();
         else evt.returnValue = false;
         if(obj.get("pane").style.display == "none") {
           for(var i = 0; i < allCalendar.length; i++) {
             allCalendar[i].close();
           }
           obj.redraw();
         }
         else {
           obj.close();
         }
       };
     },

     nextMonth: function(obj) {
       return function(evt) {
         evt = evt || window.event;
         if (evt.stopPropagation) evt.stopPropagation();
		     else evt.cancelBubble = true;
         if (evt.preventDefault) evt.preventDefault();
         else evt.returnValue = false;
         var today = obj.get("curdate");
         var mon = today.getMonth() + 1;
         var year = today.getFullYear();
         if(mon > 11) {
           mon = 0;
           year += 1;
         }
         if(mon + 1 < obj.get("monthrange")[0]) {
           mon = obj.get("monthrange")[0] - 1;
         }
         if(mon + 1 > obj.get("monthrange")[1]) {
           mon = obj.get("monthrange")[1] - 1;
         }
         if(year == today.getMonth()) {
           return;
         }

         var newdate = new Date(year, mon, today.getDate());
         /*if(obj.getEdgeMonth(newdate) == 4) {
           return;
         }*/
         obj.get("yearpane").innerHTML = year;
         obj.get("monpane").innerHTML = obj.get("montharr")[mon];
         obj.set("curdate", newdate);
         obj.redraw();
       };
     },

     preMonth: function(obj) {
       return function(evt) {
         evt = evt || window.event;
         if (evt.stopPropagation) evt.stopPropagation();
		     else evt.cancelBubble = true;
         if (evt.preventDefault) evt.preventDefault();
         else evt.returnValue = false;
         var today = obj.get("curdate");
         var mon = today.getMonth() - 1;
         if(year == today.getMonth()) {
           return;
         }
         var year = today.getFullYear();
         if(mon < 0) {
           mon = 11;
           year -= 1;
         }
         if(mon + 1 < obj.get("monthrange")[0]) {
           mon = obj.get("monthrange")[0] - 1;
         }
         if(mon + 1 > obj.get("monthrange")[1]) {
           mon = obj.get("monthrange")[1] - 1;
         }

         var newdate = new Date(year, mon, today.getDate());
         /*if(obj.getEdgeMonth(newdate) == 4) {
           return;
         }*/
         obj.get("yearpane").innerHTML = year;
         obj.get("monpane").innerHTML = obj.get("montharr")[mon];
         obj.set("curdate", newdate);
         obj.redraw();
       };
     },

     nextYear: function(obj) {
       return function(evt) {
         evt = evt || window.event;
         if (evt.stopPropagation) evt.stopPropagation();
		     else evt.cancelBubble = true;
         if (evt.preventDefault) evt.preventDefault();
         else evt.returnValue = false;
         var today = obj.get("curdate");
         var year = today.getFullYear() + 1;
         if(year < obj.get("yearrange")[0]) {
           year = obj.get("yearrange")[0];
         }
         if(year > obj.get("yearrange")[1]) {
           year = obj.get("yearrange")[1];
         }
         if(year == today.getFullYear()) {
           return;
         }
         var newdate = new Date(year, today.getMonth(), today.getDate());
         /*if(obj.getEdgeMonth(newdate) == 4) {
           return;
         }*/
         obj.get("yearpane").innerHTML = year;
         obj.set("curdate", newdate);
         obj.redraw();
       };
     },

     preYear: function(obj) {
       return function(evt) {
         evt = evt || window.event;
         if (evt.stopPropagation) evt.stopPropagation();
		     else evt.cancelBubble = true;
         if (evt.preventDefault) evt.preventDefault();
         else evt.returnValue = false;
         var today = obj.get("curdate");
         var year = today.getFullYear() - 1;
         if(year < obj.get("yearrange")[0]) {
           year = obj.get("yearrange")[0];
         }
         if(year > obj.get("yearrange")[1]) {
           year = obj.get("yearrange")[1];
         }
         if(year == today.getFullYear()) {
           return;
         }
         var newdate = new Date(year, today.getMonth(), today.getDate());
         /*if(obj.getEdgeMonth(newdate) == 4) {
           return;
         }*/
         obj.get("yearpane").innerHTML = year;
         obj.set("curdate", newdate);
         obj.redraw();
       };
     },

     gotoToday: function(obj) {
       return function() {
         obj.set("curdate", new Date());
         obj.redraw();
       };
     },

     redraw: function() {
       if(this.get("type") == "POPUP") {
         var pane = this.get("pane");

         var pos = $off(this.get("source"));
         var wh = $dim(this.get("source"));//.getDimensions();

         var left = 0;
         var top = pos.top + wh.height + 2;
         if(this.get("align") == "right") {
           left = left - (this.get("width") - wh.width + 2);
         }
         else if(this.get("align") == "center") {
           left = left - parseInt((this.get("width") - wh.width) / 2);
         }
         pane.style.left = left + "px";
         pane.style.top = top + "px";
         pane.style.zIndex = 100;
         var shadow = this.get("shadowpane");
         shadow.style.left = (left + 3) + "px";
         shadow.style.top = (top +  3) + "px";
         shadow.style.zIndex = 99;
         shadow.style.display = "";
         pane.style.display = "";
         var ifr = this.get("shadowifr");
         ifr.style.left = left + "px";
         ifr.style.top = top + "px";
         ifr.style.zIndex = 98;
         ifr.style.display = "";
       }
       var today = this.get("curdate");
       var thisYear = today.getFullYear();
       var thisMonth = today.getMonth();
       var firstDate = new Date(thisYear, thisMonth, 1);
       var firstDay = firstDate.getDay();

       var nextMonth = thisMonth + 1;
       var nextYear = thisYear;
       if(nextMonth > 11) {
         nextYear = thisYear + 1;
         nextMonth = 0;
       }
       var lastDate = new Date(nextYear, nextMonth, 1);
       var days = (lastDate - firstDate) / (24 * 3600 * 1000);
       var dayspane = this.get("dayspane");
       dayspane.innerHTML = "";
       var dwid = parseInt((this.get("width") - 7 * 4) / 7);
       var obj = this;
       var seld = this.get("seldate");
       var isSelInMonth = (seld.getFullYear() == today.getFullYear() && seld.getMonth() == today.getMonth());

       var edgeMonth = this.getEdgeMonth(this.get("curdate"), true);
       var firstd = this.get("dayrange")[0];
       var lastd = this.get("dayrange")[1];
       if(edgeMonth == 1) {
         firstd = this.get("daterange")[0].getDate();
       }
       else if(edgeMonth == 2){
         lastd = this.get("daterange")[1].getDate();
       }

       var wdayc = firstDay;
       if(wdayc == 0) {
         wdayc = 7;
       }
       for(var i = 1; i <= days; i++) {
         var div = $n("div");
         div.className = 'day';
         if(wdayc > 7) {
           wdayc = 1;
         }

         div.innerHTML = i;
         dayspane.appendChild(div);
         if(i == 1) {
           div.style.cssText = "margin-left:" + ((dwid + 4) * firstDay + 2) + "px";
         }
         if(i < firstd || i > lastd || wdayc < this.get("weekdayrange")[0] || wdayc > this.get("weekdayrange")[1] || edgeMonth == 4) {
           div.style.backgroundColor = "#EFEFEF";
           div.style.cursor = "default";
         }
         else {
           $e(div, "click", function(){
                var seldate = parseInt(this.innerHTML);
                var curd = obj.get("curdate");

                obj.set("curdate", new Date(curd.getFullYear(), curd.getMonth(), seldate));
                obj.set("seldate", new Date(curd.getFullYear(), curd.getMonth(), seldate));
                var callback = obj.get("callback");
                if(typeof callback == "function") {
                  obj.redraw();
                  obj.close();
                  obj.get("callback").apply(null, [obj.get("seldate")]);
                }
                else {
                  var sdate = obj.comDate(obj.get("seldate"));
                  obj.redraw();
                  obj.close();
                  alert(sdate.year + "." + sdate.month + "." + sdate.date);
                }
              });
         }
         if(isSelInMonth && seld.getDate() == i) {
           div.style.backgroundColor = "#FFF1A8";
         }
         else {
           var td = new Date();
           var cr = this.get("curdate");
           if(cr.getFullYear() == td.getFullYear() && cr.getMonth() == td.getMonth() && i == td.getDate()) {
             div.style.backgroundColor = "#6694E3";
             div.style.color = "white";
           }
         }
         div.style.width = (dwid) + "px";
         wdayc ++;
       }
       var clr = $n("div");
       clr.className = "clr";
       dayspane.appendChild(clr);
       var butClose = $n("div");
       butClose.innerHTML = "关闭";
       butClose.style.cssText = "position:absolute;right:15px;bottom:5px;color:blue;cursor:pointer;";
       var butAll = $n("div");
       butAll.innerHTML = "全部";
       butAll.style.cssText = "position:absolute;right:60px;bottom:5px;color:blue;cursor:pointer;";
       $e(butClose, "click", this.hide(this));
       var obj = this;
       $e(butAll, "click", function(){
                if(typeof obj.get("callback") == "function") {                  
                  obj.get("callback").apply(null);
                }
                obj.close();
              });
       dayspane.appendChild(butClose);
       dayspane.appendChild(butAll);
     },
     hide: function(obj) {
       return function() {
         obj.close();
       };
     },
     close: function() {
       if(this.get("type") == "POPUP") {
         this.get("pane").style.display = "none";
         this.get("shadowpane").style.display = "none";
         this.get("shadowifr").style.display = "none";
       }
     },

     getEdgeMonth: function(date, checkYM) {
       var today = date;
       var edgeMonth = 0;
       var dater = this.get("daterange");
       if(today.getFullYear () == dater[0].getFullYear() && today.getMonth() == dater[0].getMonth()) {
         edgeMonth = 1;
       }
       else if(today.getFullYear() ==dater[1].getFullYear() && today.getMonth() == dater[1].getMonth()) {
         edgeMonth = 2;
       }
       else if(today < dater[0] || today > dater[1]) {
         edgeMonth = 4;
       }
       else  if(checkYM){
         var fy = today.getFullYear();
         var mn = today.getMonth() + 1;
         if(fy < this.get("yearrange")[0] || fy > this.get("yearrange")[1] || mn < this.get("monthrange")[0] || mn > this.get("monthrange")[1]) {
           edgeMonth = 4;
         }
       }
       return edgeMonth;
     },

     divDate: function(dfdate) {
       var vaild = false;
       if(typeof dfdate == "string") {
         var y = parseInt(dfdate.substr(0, 4));
         var m = parseInt(dfdate.substr(4, 2));
         var d = parseInt(dfdate.substr(6, 2));
         if((!isNaN(y)) && (!isNaN(m)) && (!isNaN(d))
            && 0 < m && m < 13 && d > 0 && d < 32 ) {
           return new Date(y, m - 1, d);
         }
       }
       if(!vaild) {
         throw "Date should be a string like 'yyyymmdd'.";
       }
       return false;
     },

     comDate: function(date) {
       var mon = date.getMonth();
       return {
         'year': date.getFullYear(),
         'month': mon + 1,
         'date': date.getDate(),
         'day': date.getDay(),
         'monstr': this.get("montharr")[mon]
       };
     }
   };
   window.Calendar_ = window.Calendar_ || Calendar;
 })();
